package hibernatemock;

import java.lang.annotation.Annotation;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.validation.constraints.AssertFalse;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Digits;
import javax.validation.constraints.Future;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Null;
import javax.validation.constraints.Past;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import lombok.Setter;
import nl.flotsam.xeger.Xeger;

import org.apache.commons.lang.StringUtils;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.Range;

public class DefaultSetup {

	@Setter private SetupBuilder setupBuilder;

	public void fillWithDefaults() {
		string();
		primitiveAndWrappers();
		bigdecimal();
		date();
		calendar();
		sqlDate();
		time();
		timeStamp();
		collections();
		maps();
		arrays();
		//---- Bean Validation -----//
		notEmpty();
		assertTrue();
		assertFalse();
		email();
		future();
		past();
		nulll();
		length();
		pattern();
		min();
		max();
		decimalMin();
		decimalMax();
		digits();
		range();
		size();
	}

	private void timeStamp() {
		setupBuilder.when(Timestamp.class).deliver(new Timestamp(0));
	}

	private void time() {
		setupBuilder.when(Time.class).deliver(new Time(0));
	}

	private void sqlDate() {
		setupBuilder.when(java.sql.Date.class).deliver(new java.sql.Date(0));
	}

	private void calendar() {
		setupBuilder.when(Calendar.class).deliver(getCalendarDate0());
	}

	private void date() {
		setupBuilder.when(Date.class).deliver(new Date(0));
	}

	private void bigdecimal() {
		setupBuilder.when(BigInteger.class).deliver(new BigInteger("0"));
		setupBuilder.when(BigDecimal.class).deliver(new BigDecimal("0"));
	}

	private void arrays() {
		//Arrays
		setupBuilder.when(byte[].class).deliver(new byte[] { 0x00 });
		setupBuilder.when(char[].class).deliver(new char[] { '0' });
		setupBuilder.when(Byte[].class).deliver(new Byte[] { 0x00 });
		setupBuilder.when(Character[].class).deliver(new Character[] { '0' });
	}

	private void maps() {
		//Maps
		setupBuilder.when(Map.class, HashMap.class).deliver(new HashMap<Object, Object>());
		setupBuilder.when(LinkedHashMap.class).deliver(new LinkedHashMap<Object, Object>());
	}

	private void collections() {
		//Collections
		setupBuilder.when(Collection.class, ArrayList.class, List.class).deliver(new ArrayList<Object>());
		setupBuilder.when(LinkedList.class).deliver(new LinkedList<Object>());
		setupBuilder.when(Set.class, HashSet.class).deliver(new HashSet<Object>());
		setupBuilder.when(LinkedHashSet.class).deliver(new LinkedHashSet<Object>());
	}

	private void primitiveAndWrappers() {
		//Primitives and Wrappers
		setupBuilder.when(Boolean.class).deliver(new Boolean(false));
		setupBuilder.when(Byte.class).deliver(new Byte((byte) 0));
		setupBuilder.when(Character.class).deliver(new Character((char) 0));
		setupBuilder.when(Short.class).deliver(new Short((short) 0));
		setupBuilder.when(Integer.class).deliver(0);
		setupBuilder.when(Long.class).deliver(0L);
		setupBuilder.when(Float.class).deliver(0F);
		setupBuilder.when(Double.class).deliver(0D);
	}

	private void string() {
		//String
		setupBuilder.when(String.class).deliver("");
	}

	private void nulll() {
		setupBuilder.when(Object.class, Null.class).deliver(null);
	}

	private void past() {
		setupBuilder.when(Date.class, Past.class).deliver(subYear()); //- one year
		setupBuilder.when(Calendar.class, Past.class).deliver(getCalendar(subYear())); //- one year
	}

	private void future() {
		setupBuilder.when(Date.class, Future.class).deliver(sumYear()); //+ one year
		setupBuilder.when(Calendar.class, Future.class).deliver(getCalendar(sumYear())); //+ one year
	}

	private void email() {
		setupBuilder.when(String.class, Email.class).deliver("teste@teste.com");
	}

	private void assertFalse() {
		setupBuilder.when(Boolean.class, AssertTrue.class).deliver(new Boolean(true));
	}

	private void assertTrue() {
		setupBuilder.when(Boolean.class, AssertFalse.class).deliver(new Boolean(false));
	}

	private void notEmpty() {
		setupBuilder.when(String.class, NotEmpty.class).deliver(" ");
	}

	private void length() {
		//@Length String
		setupBuilder.when(String.class, Length.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return StringUtils.leftPad("", ((Length) annotation).min(), " ");
			}
		});
	}

	private void pattern() {
		//@Pattern String
		setupBuilder.when(String.class, Pattern.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Xeger(((Pattern) annotation).regexp()).generate();
			}
		});
	}

	private void min() {
		//@Min BigDecimal, BigInteger, byte, short, int, long
		setupBuilder.when(BigDecimal.class, Min.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new BigDecimal(((Min) annotation).value());
			}
		});
		setupBuilder.when(BigInteger.class, Min.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new BigInteger(String.valueOf(((Min) annotation).value()));
			}
		});
		setupBuilder.when(Byte.class, Min.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Byte(String.valueOf(((Min) annotation).value()));
			}
		});
		setupBuilder.when(Short.class, Min.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Short(String.valueOf(((Min) annotation).value()));
			}
		});
		setupBuilder.when(Integer.class, Min.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Integer(String.valueOf(((Min) annotation).value()));
			}
		});
		setupBuilder.when(Long.class, Min.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Long(String.valueOf(((Min) annotation).value()));
			}
		});
	}

	private void max() {
		//@Max BigDecimal, BigInteger, byte, short, int, long
		setupBuilder.when(BigDecimal.class, Max.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new BigDecimal(((Max) annotation).value());
			}
		});
		setupBuilder.when(BigInteger.class, Max.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new BigInteger(String.valueOf(((Max) annotation).value()));
			}
		});
		setupBuilder.when(Byte.class, Max.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Byte(String.valueOf(((Max) annotation).value()));
			}
		});
		setupBuilder.when(Short.class, Max.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Short(String.valueOf(((Max) annotation).value()));
			}
		});
		setupBuilder.when(Integer.class, Max.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Integer(String.valueOf(((Max) annotation).value()));
			}
		});
		setupBuilder.when(Long.class, Max.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Long(String.valueOf(((Max) annotation).value()));
			}
		});
	}

	private void decimalMin() {
		//@DecimalMin BigDecimal, BigInteger, byte, short, int, long
		setupBuilder.when(BigDecimal.class, DecimalMin.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new BigDecimal(((DecimalMin) annotation).value());
			}
		});
		setupBuilder.when(BigInteger.class, DecimalMin.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new BigInteger(String.valueOf(((DecimalMin) annotation).value()));
			}
		});
		setupBuilder.when(Byte.class, DecimalMin.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Byte(String.valueOf(((DecimalMin) annotation).value()));
			}
		});
		setupBuilder.when(Short.class, DecimalMin.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Short(String.valueOf(((DecimalMin) annotation).value()));
			}
		});
		setupBuilder.when(Integer.class, DecimalMin.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Integer(String.valueOf(((DecimalMin) annotation).value()));
			}
		});
		setupBuilder.when(Long.class, DecimalMin.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Long(String.valueOf(((DecimalMin) annotation).value()));
			}
		});
	}

	private void decimalMax() {
		//@DecimalMax BigDecimal, BigInteger, byte, short, int, long
		setupBuilder.when(BigDecimal.class, DecimalMax.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new BigDecimal(((DecimalMax) annotation).value());
			}
		});
		setupBuilder.when(BigInteger.class, DecimalMax.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new BigInteger(String.valueOf(((DecimalMax) annotation).value()));
			}
		});
		setupBuilder.when(Byte.class, DecimalMax.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Byte(String.valueOf(((DecimalMax) annotation).value()));
			}
		});
		setupBuilder.when(Short.class, DecimalMax.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Short(String.valueOf(((DecimalMax) annotation).value()));
			}
		});
		setupBuilder.when(Integer.class, DecimalMax.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Integer(String.valueOf(((DecimalMax) annotation).value()));
			}
		});
		setupBuilder.when(Long.class, DecimalMax.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Long(String.valueOf(((DecimalMax) annotation).value()));
			}
		});
	}

	private void digits() {
		//@Digits BigDecimal, BigInteger, byte, short, int, long
		setupBuilder.when(BigDecimal.class, Digits.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new BigDecimal(StringUtils.leftPad("", (int) ((Digits) annotation).integer(), "0") + "." + StringUtils.leftPad("", (int) ((Digits) annotation).fraction(), "0"));
			}
		});
		setupBuilder.when(BigInteger.class, Digits.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new BigInteger(StringUtils.leftPad("", (int) ((Digits) annotation).integer(), "0"));
			}
		});
		setupBuilder.when(Byte.class, Digits.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Byte(StringUtils.leftPad("", (int) ((Digits) annotation).integer(), "0"));
			}
		});
		setupBuilder.when(Short.class, Digits.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Short(StringUtils.leftPad("", (int) ((Digits) annotation).integer(), "0"));
			}
		});
		setupBuilder.when(Integer.class, Digits.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Integer(StringUtils.leftPad("", (int) ((Digits) annotation).integer(), "0"));
			}
		});
		setupBuilder.when(Long.class, Digits.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Long(StringUtils.leftPad("", (int) ((Digits) annotation).integer(), "0"));
			}
		});

		setupBuilder.when(String.class, Range.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return StringUtils.leftPad("", (int) ((Range) annotation).min(), " ");
			}
		});
	}

	private void range() {
		//@Range BigDecimal, BigInteger, byte, short, int, long
		setupBuilder.when(BigDecimal.class, Range.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new BigDecimal(((Range) annotation).min());
			}
		});
		setupBuilder.when(BigInteger.class, Range.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new BigInteger(String.valueOf(((Range) annotation).min()));
			}
		});
		setupBuilder.when(Byte.class, Range.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Byte(String.valueOf(((Range) annotation).min()));
			}
		});
		setupBuilder.when(Short.class, Range.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Short(String.valueOf(((Range) annotation).min()));
			}
		});
		setupBuilder.when(Integer.class, Range.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Integer(String.valueOf(((Range) annotation).min()));
			}
		});
		setupBuilder.when(Long.class, Range.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Long(String.valueOf(((Range) annotation).min()));
			}
		});
	}

	private void size() {
		//@Size String, Collection, Map and arrays.
		setupBuilder.when(String.class, Size.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return StringUtils.leftPad("", ((Size) annotation).min(), " ");
			}
		});
		setupBuilder.when(Collection.class, ArrayList.class, List.class, Size.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				Collection<Object> collection = new ArrayList<Object>();
				for (int i = 0; i < ((Size) annotation).min(); i++)
					collection.add(new Object());
				return collection;
			}
		});
		setupBuilder.when(LinkedList.class, Size.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				Collection<Object> collection = new LinkedList<Object>();
				for (int i = 0; i < ((Size) annotation).min(); i++)
					collection.add(new Object());
				return collection;
			}
		});
		setupBuilder.when(Set.class, HashSet.class, Size.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				Collection<Object> collection = new HashSet<Object>();
				for (int i = 0; i < ((Size) annotation).min(); i++)
					collection.add(new Object());
				return collection;
			}
		});
		setupBuilder.when(LinkedHashSet.class, Size.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				Collection<Object> collection = new LinkedHashSet<Object>();
				for (int i = 0; i < ((Size) annotation).min(); i++)
					collection.add(new Object());
				return collection;
			}
		});
		setupBuilder.when(Map.class, HashMap.class, Size.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				Map<Object, Object> map = new HashMap<Object, Object>();
				for (int i = 0; i < ((Size) annotation).min(); i++)
					map.put(new Object(), new Object());
				return map;
			}
		});
		setupBuilder.when(LinkedHashMap.class, Size.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				Map<Object, Object> map = new LinkedHashMap<Object, Object>();
				for (int i = 0; i < ((Size) annotation).min(); i++)
					map.put(new Object(), new Object());
				return map;
			}
		});
		setupBuilder.when(byte[].class, Size.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new byte[((Size) annotation).min()];
			}
		});
		setupBuilder.when(Byte[].class, Size.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Byte[((Size) annotation).min()];
			}
		});
		setupBuilder.when(char[].class, Size.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new char[((Size) annotation).min()];
			}
		});
		setupBuilder.when(Character[].class, Size.class).deliver(new FieldHandler() {
			@Override public Object deliver(Annotation annotation) {
				return new Character[((Size) annotation).min()];
			}
		});
	}

	private Date sumYear() {
		return new Date(System.currentTimeMillis() + 31556952000L);
	}

	private Date subYear() {
		return new Date(System.currentTimeMillis() - 31556952000L);
	}

	private Calendar getCalendar(Date date) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		return calendar;
	}

	private Calendar getCalendarDate0() {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date(0));
		return calendar;
	}

}
